--dude its just like soinc 2 holy shit!

addHook("AbilitySpecial", function(p)
	if not mapheaderinfo[gamemap].s2gg then return end
	if p.charability == 1 then
		return true
	end
end)

addHook("SpinSpecial", function(p)
	if not mapheaderinfo[gamemap].s2gg then return end
	return true
end)

addHook("PreThinkFrame", do
	if not mapheaderinfo[gamemap].s2gg then return end
	for p in players.iterate do
		if p.mo and p.mo.valid then
			p.s2g_slopedir = $ or 0
			p.s2g_slopeheight = $ or 0			
			if p.mo.standingslope then
				p.s2g_slopedir = p.mo.standingslope.xydirection
				p.s2g_slopeheight = abs(p.mo.standingslope.zangle)/ANG1
				p.mo.sloppy = 1
				if (p.mo.eflags & MFE_JUSTHITFLOOR) 
					p.mo.momx = $*2/3
				end
			else
				if p.mo.sloppy and not P_IsObjectOnGround(p.mo) then
					if (p.pflags & PF_JUMPED) then
						p.mo.momx = $-FixedMul((p.s2g_slopeheight*FU)/3,cos(p.s2g_slopedir))
					end
				end
				p.mo.sloppy = nil
			end			
		end
	end
end)

addHook("PostThinkFrame", do
	if not mapheaderinfo[gamemap].s2gg then return end
	for p in players.iterate do
		if p.mo and p.mo.valid then
			if not p.s2g_lockcam then
				
				if (p.mo.s2g_lastx - p.mo.x) > 24*FRACUNIT then
					p.mo.s2g_lastx = $ - max(-40*FRACUNIT,min(40*FRACUNIT,p.mo.s2g_lastx - p.mo.x-24*FRACUNIT))
				elseif (p.mo.s2g_lastx - p.mo.x) < -8*FRACUNIT then
					p.mo.s2g_lastx = $ - max(-40*FRACUNIT,min(40*FRACUNIT,p.mo.s2g_lastx - p.mo.x+8*FRACUNIT))
				end
				
				if (p.mo.s2g_lastz - p.mo.z) > 64*FRACUNIT then
					p.mo.s2g_lastz = $ - max(-40*FRACUNIT,min(40*FRACUNIT,p.mo.s2g_lastz - p.mo.z-64*FRACUNIT))
				elseif (p.mo.s2g_lastz - p.mo.z) < -16*FRACUNIT then
					p.mo.s2g_lastz = $ - max(-40*FRACUNIT,min(40*FRACUNIT,p.mo.s2g_lastz - p.mo.z+16*FRACUNIT))
				end
				
			end
			if (p.playerstate == PST_DEAD) then
				p.s2g_lockcam = 1
			else
				p.s2g_lockcam = nil
			end
		end
	end
end)

addHook("PlayerThink", function(p)
	if not p.s2g_skin or mapheaderinfo[gamemap].s2gg then return end
	if p.mo and p.mo.valid then
		R_SetPlayerSkin(p,p.s2g_skin)
		p.s2g_skin = nil
	end
end)

addHook("PlayerThink", function(p)
	if not mapheaderinfo[gamemap].s2gg then return end
	if p.mo and p.mo.valid then
		local skin = skins[p.mo.skin]
		local FU = FRACUNIT
		p.s2g_anim = "S2GSTND"
		p.mo.s2g_lastx = $ or p.mo.x
		p.mo.s2g_lastz = $ or p.mo.z+32*FRACUNIT
		p.s2g_skin = $ or "sonic"
		
		if p.mo.skin != "sonic" then
			p.s2g_skin = p.skin
			R_SetPlayerSkin(p,"sonic")
		end
		
		p.normalspeed = skin.normalspeed*2/3
		p.runspeed = skin.runspeed*2/3
		p.maxdash = skin.maxdash/2
		p.mindash = skin.mindash/2
		p.actionspd = skin.actionspd*2/3
		p.jumpfactor = skin.jumpfactor*16/10
		if P_IsObjectOnGround(p.mo) then
			p.accelstart = skin.accelstart*10/11
			p.acceleration = skin.acceleration*10/13
		else
			p.accelstart = skin.accelstart*3/2
			p.acceleration = skin.acceleration/2
			p.mo.momz = $ + P_GetMobjGravity(p.mo)*2/3
		end
		
		--ending walk
		if p.exiting and p.exiting < 50 and not multiplayer then
			p.mo.momx = $+FRACUNIT
			p.mo.state = S_PLAY_WALK
			p.mo.angle = 0
			p.drawangle = 0
			P_MovePlayer(p)
		end
		
		if (p.pflags & PF_SPINNING) then
			p.drawangle = R_PointToAngle2(0,0,p.mo.momx,0)
			p.mo.angle = R_PointToAngle2(0,0,p.mo.momx,0)
			if not P_IsObjectOnGround(p.mo) then
				p.pflags = $|PF_JUMPED & ~PF_SPINNING
			end
		end
		
		if p.skidtime > 0 and P_GetPlayerControlDirection(p) == 1 and P_IsObjectOnGround(p.mo) then
			p.skidtime = 0
			p.mo.state = S_PLAY_WALK
		end
		
		if (p.mo.state == S_PLAY_WALK) or (p.mo.state == S_PLAY_FALL) then
			p.s2g_anim = "WALK"..tostring((leveltime%24)/4+1)
		elseif p.mo.state == S_PLAY_RUN then
			p.s2g_anim = "RUN"..tostring((leveltime%12)/3+1)
		elseif (p.mo.state == S_PLAY_JUMP) or (p.mo.state == S_PLAY_ROLL) or (p.mo.state == S_PLAY_SPINDASH) then
			p.s2g_anim = "ROLL"..tostring((leveltime%16)/2+1)
		elseif p.mo.state == S_PLAY_SKID then
			p.drawangle = R_PointToAngle2(0,0,p.mo.momx,0)
			p.mo.angle = R_PointToAngle2(0,0,p.mo.momx,0)
			p.s2g_anim = "SKID"..tostring((leveltime%8)/4+1)
		elseif p.mo.state == S_PLAY_EDGE then
			p.s2g_anim = "EDGE"..tostring((leveltime%32)/16+1)
		elseif p.mo.state == S_PLAY_WAIT then
			p.s2g_anim = "WAIT"..tostring((leveltime%32)/16+1)
			if abs(p.cmd.forwardmove) > 30 then
				p.mo.state = S_PLAY_STND
			end
		elseif p.mo.state == S_PLAY_SPRING then
			p.s2g_anim = "SPNG"
		elseif p.mo.state == S_PLAY_PAIN then
			p.s2g_anim = "PAIN1"
		elseif p.mo.state == S_PLAY_DEAD then
			p.s2g_anim = "DEAD"
		else
			p.s2g_anim = "STND"
			if P_IsObjectOnGround(p.mo) then
				if p.cmd.forwardmove > 30 then
					p.s2g_anim = "LKUP"
					p.mo.state = S_PLAY_FLOAT
					p.mo.s2g_lastz = $+4*FRACUNIT
				elseif p.cmd.forwardmove < -30 then
					p.s2g_anim = "CRCH"
					p.mo.state = S_PLAY_FLOAT
					p.mo.s2g_lastz = $-4*FRACUNIT
				end
			end
		end
		
		if p.drawangle == ANGLE_180 then
			p.s2g_flip = V_FLIP
		else
			p.s2g_flip = 0
		end
		
	end
end)

local function s2gg_objects(v, p)
	if not mapheaderinfo[gamemap].s2gg then return end
	if splitscreen and p != consoleplayer then return end

	local width = (v.width()/v.dupx())*FRACUNIT
	local height = (v.height()/v.dupy())*FRACUNIT
	local camx = min(10752*FU-width,max(width,p.mo.s2g_lastx))
	local camy = max(-1472*FU+height,min(-32*FRACUNIT-height,p.mo.s2g_lastz))
	
	for m in mobjs.iterate() do
		if m and m.valid then
			if m.s2g_sprite then
				--v.drawScaled((width+m.x-camx)/2,(height+camy-m.z)/2+16*FRACUNIT,m.scale,v.cachePatch("S2G"..m.s2g_sprite),V_SNAPTOLEFT|V_SNAPTOTOP,v.getColormap(TC_DEFAULT, m.color))
				v.drawScaled(FixedFloor((width+m.x-camx)/2),FixedFloor((height+camy-m.z)/2+16*FRACUNIT),m.scale,v.cachePatch("S2G"..m.s2g_sprite),V_SNAPTOLEFT|V_SNAPTOTOP,v.getColormap(TC_DEFAULT, m.color))
			--else
				--v.drawScaled((width+m.x-camx)/2,(height+camy-m.z)/2+16*FRACUNIT,m.scale/2,v.getSpritePatch(m.sprite,m.frame,3,0),V_SNAPTOLEFT|V_SNAPTOTOP,v.getColormap(TC_DEFAULT, m.color))
			end
		end
	end
end

local function s2gg_player(v, p)
	if not mapheaderinfo[gamemap].s2gg then return end
	if splitscreen and p != consoleplayer then return end
	--render the player
	local width = (v.width()/v.dupx())*FRACUNIT
	local height = (v.height()/v.dupy())*FRACUNIT
	local camx = min(10752*FU-width,max(width,p.mo.s2g_lastx))
	local camy = max(-1472*FU+height,min(-32*FRACUNIT-height,p.mo.s2g_lastz))
	for o in players.iterate do
		if o != p then
			if not (o.powers[pw_flashing] and leveltime%2 == 1) then
				v.drawScaled((width+o.mo.x-camx)/2,(height+camy-o.mo.z)/2,o.mo.scale,v.cachePatch("S2G"..o.s2g_anim),o.s2g_flip|V_SNAPTOLEFT|V_SNAPTOTOP,v.getColormap(TC_DEFAULT, o.mo.color))
			end
		end
		if not (p.powers[pw_flashing] and leveltime%2 == 1) then
			--v.drawScaled((width+p.mo.x-camx)/2,(height+camy-p.mo.z)/2,p.mo.scale,v.cachePatch("S2G"..p.s2g_anim),p.s2g_flip|V_SNAPTOLEFT|V_SNAPTOTOP,v.getColormap(TC_DEFAULT, p.mo.color))
			v.drawScaled(FixedFloor((width+p.mo.x-camx)/2),FixedFloor((height+camy-p.mo.z)/2),p.mo.scale,v.cachePatch("S2G"..p.s2g_anim),p.s2g_flip|V_SNAPTOLEFT|V_SNAPTOTOP,v.getColormap(TC_DEFAULT, p.mo.color))
		end
	end
end
-- -1344
local function s2gg_map(v, p)
    if not mapheaderinfo[gamemap].s2gg then return end
	if splitscreen and p != consoleplayer then return end
	local width = (v.width()/v.dupx())*FRACUNIT
	local height = (v.height()/v.dupy())*FRACUNIT
	local camx = min(10752*FU-width,max(width,p.mo.s2g_lastx))
	local camy = max(-1472*FU+height+32*FRACUNIT,min(-height,p.mo.s2g_lastz+32*FRACUNIT))
	for i=0,3 do
		--v.drawCropped(0,0,FRACUNIT,FRACUNIT,v.cachePatch("S2GUGZ"..i+1),V_SNAPTOLEFT|V_SNAPTOTOP|V_PERPLAYER,nil,(camx-width+(2688*FU*i))/2,(-camy-height)/2,width,height)
		--v.drawScaled(160*FRACUNIT-camx/2,100*FRACUNIT+camy/2,FRACUNIT,v.cachePatch("S2GUGZ"..i+1))
		v.drawScaled(FixedFloor(160*FRACUNIT-camx/2),FixedFloor(100*FRACUNIT+camy/2),FRACUNIT,v.cachePatch("S2GUGZ"..i+1))
	end
end

local function s2gg_hud(v, p)
    if not mapheaderinfo[gamemap].s2gg then return end
	if splitscreen and p != consoleplayer then return end
	local ringoffset
	if p.rings > 99 then
		ringoffset = 8*FRACUNIT
		if p.rings > 999 then
			ringoffset = 16*FRACUNIT
		end
	else
		ringoffset = 0
	end
	--rings
	v.drawScaled(8*FRACUNIT,1*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUDR"),V_SNAPTOLEFT|V_SNAPTOTOP)--ring
	v.drawScaled(ringoffset+24*FRACUNIT,1*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring((p.rings%100)/10)),V_SNAPTOLEFT|V_SNAPTOTOP)--tens
	v.drawScaled(ringoffset+32*FRACUNIT,1*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(p.rings%10)),V_SNAPTOLEFT|V_SNAPTOTOP)--ones
	if p.rings > 99 then
		v.drawScaled(ringoffset+16*FRACUNIT,1*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(p.rings%1000)/100),V_SNAPTOLEFT|V_SNAPTOTOP)--hundos
		if p.rings > 999 then
			v.drawScaled(24*FRACUNIT,1*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(p.rings/1000)),V_SNAPTOLEFT|V_SNAPTOTOP)--thousands
		end
	end
	--time
	v.drawScaled(8*FRACUNIT,17*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(min(9,G_TicsToMinutes(p.realtime)))),V_SNAPTOLEFT|V_SNAPTOTOP)--minutes
	v.drawScaled(16*FRACUNIT,17*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUDC"),V_SNAPTOLEFT|V_SNAPTOTOP)--colon
	v.drawScaled(24*FRACUNIT,17*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(G_TicsToSeconds(p.realtime)/10)),V_SNAPTOLEFT|V_SNAPTOTOP)--tens
	v.drawScaled(32*FRACUNIT,17*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(G_TicsToSeconds(p.realtime)%10)),V_SNAPTOLEFT|V_SNAPTOTOP)--tens
	--life counter
	v.drawScaled(8*FRACUNIT,184*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUDL"),V_SNAPTOLEFT|V_SNAPTOBOTTOM,v.getColormap(TC_DEFAULT, p.mo.color))
	v.drawScaled(24*FRACUNIT,184*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUDX"),V_SNAPTOLEFT|V_SNAPTOBOTTOM)--x
	v.drawScaled(min(1,((p.lives%100)/10))*8*FRACUNIT+32*FRACUNIT,184*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(p.lives%10)),V_SNAPTOLEFT|V_SNAPTOBOTTOM)--ones
	if p.lives > 9 then
		v.drawScaled(32*FRACUNIT,184*FRACUNIT,FRACUNIT,v.cachePatch("S2GHUD"..tostring(p.lives%100)/10),V_SNAPTOLEFT|V_SNAPTOBOTTOM)--tens
	end
end

hud.add(s2gg_map, "game")
hud.add(s2gg_objects, "game")
hud.add(s2gg_player, "game")
hud.add(s2gg_hud, "game")